package com.bjsxt.homework3;

import java.util.ArrayList;
import java.util.List;

public class MyContainer {

    //数据载体
    List<Object> list = new ArrayList<>();

    //写锁
    final Object writeLock = new Object();

    //是否有线程在执行写操作
    boolean isWrite = false;

    //是否有线程在执行读操作
    boolean isRead = false;

    public void add(Object obj) {

        //判断有无其他线程正在执行读操作
        while (isRead) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //同步代码块，锁定写锁
        synchronized (writeLock) {

            try {
                //判断是否有其他线程正在执行写操作
                while (isWrite) {
                    //如果有其他线程正在执行写操作，则阻塞等待。（双保险）
                    try {
                        writeLock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //当前线程执行写操作
                isWrite = true;
                list.add(obj);

                System.out.println(Thread.currentThread().getName() + " add " + obj);

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //唤醒其他写操作的线程
                writeLock.notifyAll();

            } finally {
                //此处可以不使用finally，因为synchronized本身就能保证原子性。
                isWrite = false;
            }
        }
    }

    public Object get(int index) {

        if (index < 0 || index >= list.size()) {
            throw new IndexOutOfBoundsException();
        }

        //若有线程正在执行写操作，则阻塞等待。（此处使用自旋锁，提高效率）
        while (isWrite) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        try {
            //当前线程执行读操作
            isRead = true;

            Object value = list.get(index);

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return value;

        } finally {
            //保证 get和 return是原子操作，避免可能出现的CPU时间片到期切换导致数据不一致的问题
            //原子操作指的是不可分割的操作，即该操作在执行过程中不会被线程调度机制中断。
            //一旦开始，就必须保证它能连续地完成。
            //原子操作是并发编程中的一个基本概念，用于确保多线程环境下数据的一致性和正确性。
            isRead = false;
        }
    }
}
